using System;
using System.Text;
using System.Collections;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;

namespace MSHelpLib.HXS
{
    /// <summary>
    /// 
    /// </summary>
	public class DOSHeader
	{
        /// <summary>
        /// 
        /// </summary>
		public UInt16 	magic;                     // Magic number
        /// <summary>
        /// 
        /// </summary>
		public UInt16 	cblp;                      // Bytes on last page of file
        /// <summary>
        /// 
        /// </summary>
		public UInt16 	cp;                        // Pages in file
        /// <summary>
        /// 
        /// </summary>
		public UInt16 	crlc;                      // Relocations
        /// <summary>
        /// 
        /// </summary>
		public UInt16 	cparhdr;                   // Size of header in paragraphs
        /// <summary>
        /// 
        /// </summary>
		public UInt16 	minalloc;                  // Minimum extra paragraphs needed
        /// <summary>
        /// 
        /// </summary>
		public UInt16 	maxalloc;                  // Maximum extra paragraphs needed
        /// <summary>
        /// 
        /// </summary>
		public UInt16 	ss;                        // Initial (relative) SS value
        /// <summary>
        /// 
        /// </summary>
		public UInt16 	sp;                        // Initial SP value
        /// <summary>
        /// 
        /// </summary>
		public UInt16	csum;                      // Checksum
        /// <summary>
        /// 
        /// </summary>
		public UInt16	ip;                        // Initial IP value
        /// <summary>
        /// 
        /// </summary>
		public UInt16 	cs;                        // Initial (relative) CS value
        /// <summary>
        /// 
        /// </summary>
		public UInt16 	lfarlc;                    // File address of relocation table
        /// <summary>
        /// 
        /// </summary>
		public UInt16	ovno;                      // Overlay number		
        /// <summary>
        /// 
        /// </summary>
		public UInt16[] res;					   // Reserved words x 4
        /// <summary>
        /// 
        /// </summary>
		public UInt16 	oemid;                     // OEM identifier (for e_oeminfo)
        /// <summary>
        /// 
        /// </summary>
		public UInt16 	oeminfo;                   // OEM information; e_oemid specific		
        /// <summary>
        /// 
        /// </summary>
		public UInt16[] res2;                      // Reserved words x 10 
        /// <summary>
        /// 
        /// </summary>
		public UInt32	lfanew;

        /// <summary>
        /// Reads the header.
        /// </summary>
        /// <param name="st">The st.</param>
        /// <returns></returns>
		public bool ReadHeader(BinaryReader st)
		{
			magic=st.ReadUInt16();						
			cblp=st.ReadUInt16();
			cp=st.ReadUInt16();
			crlc=st.ReadUInt16();
			cparhdr=st.ReadUInt16();
			minalloc=st.ReadUInt16();
			maxalloc=st.ReadUInt16();
			ss=st.ReadUInt16();
			sp=st.ReadUInt16();
			csum=st.ReadUInt16();
			ip=st.ReadUInt16();
			cs=st.ReadUInt16();
			lfarlc=st.ReadUInt16();
			ovno=st.ReadUInt16();
			
			res=new UInt16[4];
			res[0]=st.ReadUInt16();
			res[1]=st.ReadUInt16();
			res[2]=st.ReadUInt16();
			res[3]=st.ReadUInt16();

			oemid=st.ReadUInt16();
			oeminfo=st.ReadUInt16();

			res2=new UInt16[10];
			res2[0]=st.ReadUInt16();
			res2[1]=st.ReadUInt16();
			res2[2]=st.ReadUInt16();
			res2[3]=st.ReadUInt16();
			res2[4]=st.ReadUInt16();
			res2[5]=st.ReadUInt16();
			res2[6]=st.ReadUInt16();
			res2[7]=st.ReadUInt16();
			res2[8]=st.ReadUInt16();
			res2[9]=st.ReadUInt16();

			lfanew=st.ReadUInt32();	

			return true;
		}
	}

    /// <summary>
    /// 
    /// </summary>
	public class CoffHeader
	{
        /// <summary>
        /// 
        /// </summary>
		public UInt16	MachineType;
        /// <summary>
        /// 
        /// </summary>
		public UInt16	NumberOfSections;
        /// <summary>
        /// 
        /// </summary>
		public UInt32	DateTimeStamp;
        /// <summary>
        /// 
        /// </summary>
		public UInt32	PointerToSymbolTable;
        /// <summary>
        /// 
        /// </summary>
		public UInt32	NumberOfSymbols;
        /// <summary>
        /// 
        /// </summary>
		public UInt16	OptionalHeaderSize;
        /// <summary>
        /// 
        /// </summary>
		public UInt16	Characteristics;

        /// <summary>
        /// Reads the header.
        /// </summary>
        /// <param name="st">The st.</param>
        /// <returns></returns>
		public bool ReadHeader(BinaryReader st)
		{
			MachineType=st.ReadUInt16();
			NumberOfSections=st.ReadUInt16();
			DateTimeStamp=st.ReadUInt32();
			PointerToSymbolTable=st.ReadUInt32();
			NumberOfSymbols=st.ReadUInt32();
			OptionalHeaderSize=st.ReadUInt16();
			Characteristics=st.ReadUInt16();
			return true;
		}
	}

    /// <summary>
    /// 
    /// </summary>
	public class DataDirectoryEntry
	{
        /// <summary>
        /// 
        /// </summary>
		public UInt32	RVA;
        /// <summary>
        /// 
        /// </summary>
		public UInt32	Size;

        /// <summary>
        /// Reads the header.
        /// </summary>
        /// <param name="st">The st.</param>
        /// <returns></returns>
		public bool ReadHeader(BinaryReader st)
		{
			RVA=st.ReadUInt32();
			Size=st.ReadUInt32();
			return true;
		}
	}

    /// <summary>
    /// 
    /// </summary>
	public class OptionalHeader
	{
        /// <summary>
        /// 
        /// </summary>
		public bool		bPE32Plus;
        /// <summary>
        /// 
        /// </summary>
		public UInt16	Magic;		// 0x10b = PE32, 0x20b=PE32+
        /// <summary>
        /// 
        /// </summary>
		public byte		MajorLinkerVersion;
        /// <summary>
        /// 
        /// </summary>
		public byte		MinorLinkerVersion;
        /// <summary>
        /// 
        /// </summary>
		public UInt32	SizeOfCode;
        /// <summary>
        /// 
        /// </summary>
		public UInt32	SizeOfInitData;
        /// <summary>
        /// 
        /// </summary>
		public UInt32	SizeOfUnInitData;
        /// <summary>
        /// 
        /// </summary>
		public UInt32	AddressOfEntryPoint;
        /// <summary>
        /// 
        /// </summary>
		public UInt32	BaseOfCode;

        /// <summary>
        /// 
        /// </summary>
		public UInt32	BaseOfData;		// not in PE32+

		// Windows optional header fields.
        /// <summary>
        /// 
        /// </summary>
		public UInt64	ImageBase;
        /// <summary>
        /// 
        /// </summary>
		public UInt32	SectionAlignment;
        /// <summary>
        /// 
        /// </summary>
		public UInt32	FileAlignment;
        /// <summary>
        /// 
        /// </summary>
		public UInt16	MajorOperatingSystemVersion;
        /// <summary>
        /// 
        /// </summary>
		public UInt16	MinorOperatingSystemVersion;
        /// <summary>
        /// 
        /// </summary>
		public UInt16	MajorImageVersion;
        /// <summary>
        /// 
        /// </summary>
		public UInt16	MinorImageVersion;
        /// <summary>
        /// 
        /// </summary>
		public UInt16	MajorSubsystemVersion;
        /// <summary>
        /// 
        /// </summary>
		public UInt16	MinorSubsystemVersion;
        /// <summary>
        /// 
        /// </summary>
		public UInt32	Reserved;
        /// <summary>
        /// 
        /// </summary>
		public UInt32	SizeOfImage;
        /// <summary>
        /// 
        /// </summary>
		public UInt32	SizeOfHeaders;
        /// <summary>
        /// 
        /// </summary>
		public UInt32	CheckSum;
        /// <summary>
        /// 
        /// </summary>
		public UInt16	Subsystem;
        /// <summary>
        /// 
        /// </summary>
		public UInt16	DLLCharacteristics;
        /// <summary>
        /// 
        /// </summary>
		public UInt64	SizeOfStackReserve;
        /// <summary>
        /// 
        /// </summary>
		public UInt64	SizeOfStackCommit;
        /// <summary>
        /// 
        /// </summary>
		public UInt64	SizeOfHeapReserve;
        /// <summary>
        /// 
        /// </summary>
		public UInt64	SizeOfHeapCommit;
        /// <summary>
        /// 
        /// </summary>
		public UInt32	LoaderFlags;
        /// <summary>
        /// 
        /// </summary>
		public UInt32	NumberOfRvaAndSizes;

        /// <summary>
        /// 
        /// </summary>
		public ArrayList	DataDirectories;

        /// <summary>
        /// Reads the header.
        /// </summary>
        /// <param name="st">The st.</param>
        /// <returns></returns>
		public bool ReadHeader(BinaryReader st)
		{
			Magic=st.ReadUInt16();
			if (Magic==0x10b)
				bPE32Plus=false;
			if (Magic==0x20b)
				bPE32Plus=true;
		
			MajorLinkerVersion=st.ReadByte();
			MinorLinkerVersion=st.ReadByte();
			SizeOfCode=st.ReadUInt32();
			SizeOfInitData=st.ReadUInt32();
			SizeOfUnInitData=st.ReadUInt32();
			AddressOfEntryPoint=st.ReadUInt32();
			BaseOfCode=st.ReadUInt32();

			if (bPE32Plus==false)
				BaseOfData=st.ReadUInt32();
			
			if (bPE32Plus)
			{
				ImageBase=st.ReadUInt64();
			}
			else
			{
				ImageBase=st.ReadUInt32();
			}

			SectionAlignment=st.ReadUInt32();
			FileAlignment=st.ReadUInt32();
			MajorOperatingSystemVersion=st.ReadUInt16();
			MinorOperatingSystemVersion=st.ReadUInt16();
			MajorImageVersion=st.ReadUInt16();
			MinorImageVersion=st.ReadUInt16();
			MajorSubsystemVersion=st.ReadUInt16();
			MinorSubsystemVersion=st.ReadUInt16();
			Reserved=st.ReadUInt32();
			SizeOfImage=st.ReadUInt32();
			SizeOfHeaders=st.ReadUInt32();
			CheckSum=st.ReadUInt32();
			Subsystem=st.ReadUInt16();
			DLLCharacteristics=st.ReadUInt16();

			if (bPE32Plus)
			{			
				SizeOfStackReserve=st.ReadUInt64();
				SizeOfStackCommit=st.ReadUInt64();
				SizeOfHeapReserve=st.ReadUInt64();
				SizeOfHeapCommit=st.ReadUInt64();
			}
			else
			{			
				SizeOfStackReserve=st.ReadUInt32();
				SizeOfStackCommit=st.ReadUInt32();
				SizeOfHeapReserve=st.ReadUInt32();
				SizeOfHeapCommit=st.ReadUInt32();
			}
			LoaderFlags=st.ReadUInt32();
			NumberOfRvaAndSizes=st.ReadUInt32();

			// Read RVA's
			DataDirectories=new ArrayList();
			for(int i=0;i<NumberOfRvaAndSizes;i++)
			{
				DataDirectoryEntry DDE=new DataDirectoryEntry();
				DDE.ReadHeader(st);
				DataDirectories.Add(DDE);
			}

			return true;
		}
	}

    /// <summary>
    /// 
    /// </summary>
	public class SectionHeader
	{
        /// <summary>
        /// 
        /// </summary>
		public string	FieldName;
        /// <summary>
        /// 
        /// </summary>
		public UInt32	VirtualSize;
        /// <summary>
        /// 
        /// </summary>
		public UInt32	VirtualAddress;
        /// <summary>
        /// 
        /// </summary>
		public UInt32	SizeOfRawData;
        /// <summary>
        /// 
        /// </summary>
		public UInt32	PointerToRawData;
        /// <summary>
        /// 
        /// </summary>
		public UInt32	PointerToRelocations;
        /// <summary>
        /// 
        /// </summary>
		public UInt32	PointerToLineNumbers;
        /// <summary>
        /// 
        /// </summary>
		public UInt16	NumberOfRelocations;
        /// <summary>
        /// 
        /// </summary>
		public UInt16	NumberOfLineNumbers;
        /// <summary>
        /// 
        /// </summary>
		public UInt32	Characteristics;

        /// <summary>
        /// Reads the header.
        /// </summary>
        /// <param name="st">The st.</param>
        /// <returns></returns>
		public bool ReadHeader(BinaryReader st)
		{						
			byte [] SecName=st.ReadBytes(8);
			int j=0;
			for(j=0;j<SecName.Length;j++)
			{
				if (SecName[j]==0)
					break;
			}
			FieldName=Encoding.ASCII.GetString(SecName,0,j);
			VirtualSize=st.ReadUInt32();
			VirtualAddress=st.ReadUInt32();
			SizeOfRawData=st.ReadUInt32();
			PointerToRawData=st.ReadUInt32();
			PointerToRelocations=st.ReadUInt32();
			PointerToLineNumbers=st.ReadUInt32();
			NumberOfRelocations=st.ReadUInt16();
			NumberOfLineNumbers=st.ReadUInt16();
			Characteristics=st.ReadUInt32();

			return true;
		}
	}

	/// <summary>
	/// Summary description for PEFile.
	/// </summary>
	public class PEFile
	{
		private BinaryReader st=null;
		private long m_StartPos=0;

        /// <summary>
        /// Initializes a new instance of the <see cref="PEFile"/> class.
        /// </summary>
        /// <param name="stream">The stream.</param>
		public PEFile(BinaryReader stream)
		{			
			st=stream;
			if (st==null)
				throw new ArgumentNullException("st","Parameter st is null");
			m_StartPos=st.BaseStream.Position;
		}

		/// <summary>
		/// Check to see if file is an MZ File (PE File format)
		/// </summary>
		/// <returns>Returns true if file starts with 'MZ', else returns false. The file read position is not changed</returns>
		public bool IsMZFile()
		{
			byte [] FileID=st.ReadBytes(2);			
			st.BaseStream.Seek(0,SeekOrigin.Begin);

			if ((FileID[0]=='M') && (FileID[1]=='Z'))
				return true;
			else
				return false;			
		}

		
		private DOSHeader dosHeader=null;
        /// <summary>
        /// Reads the dos image header.
        /// </summary>
        /// <returns></returns>
		public bool ReadDosImageHeader()
		{
			if (dosHeader!=null)
				return true;
			dosHeader=new DOSHeader();
			return dosHeader.ReadHeader(st);
		}

        /// <summary>
        /// Reads the PE sig.
        /// </summary>
        /// <returns></returns>
		public bool ReadPESig()
		{
			byte [] PESig=st.ReadBytes(4);
			if ((PESig[0]=='P') && (PESig[1]=='E') && (PESig[2]==0) && (PESig[3]==0))
				return true;
			else
				return false;
		}
		
		private CoffHeader coffHeader=null;
        /// <summary>
        /// Reads the COFF header.
        /// </summary>
        /// <returns></returns>
		public bool ReadCOFFHeader()
		{
			if (coffHeader!=null)
				return true;

			coffHeader=new CoffHeader();
			return coffHeader.ReadHeader(st);
		}

		private OptionalHeader optionalHeader=null;
        /// <summary>
        /// Reads the optional header.
        /// </summary>
        /// <returns></returns>
		public bool ReadOptionalHeader()
		{			
			if (optionalHeader!=null)
				return true;

			if (coffHeader==null)
				return false;

			if (coffHeader.OptionalHeaderSize==0)
				return true;

			optionalHeader=new OptionalHeader();
			return optionalHeader.ReadHeader(st);						
		}

        /// <summary>
        /// 
        /// </summary>
		public ArrayList SectionTable=null;
        /// <summary>
        /// Reads the section headers.
        /// </summary>
        /// <returns></returns>
		public bool ReadSectionHeaders()
		{
			if (SectionTable!=null)
				return true;

			if (coffHeader==null)
				return false;

			SectionTable=new ArrayList();
			for(int i=0;i<coffHeader.NumberOfSections;i++)
			{
				SectionHeader sectionHeader=new SectionHeader();
				sectionHeader.ReadHeader(st);
				SectionTable.Add(sectionHeader);
			}

			return true;
		}


		/// <summary>
		/// Read PE File and moves to the start of Section specified
		/// </summary>
		/// <param name="SectionName">Name of section to move to</param>
		/// <returns>Returns true if we have moved to the start of the section specified, else returns false</returns>
		public bool MoveToStartOfSection(string SectionName)
		{
			// Read PE File & find start of Section.
			if (!ReadDosImageHeader()) return false;
			if (!ReadPESig()) return false;
			if (!ReadCOFFHeader()) return false;
			if (!ReadOptionalHeader()) return false;
			if (!ReadSectionHeaders()) return false;

			foreach(SectionHeader SecHeader in SectionTable)
			{
				if (SecHeader.FieldName.ToLower().CompareTo(SectionName.ToLower())==0)
				{
					st.BaseStream.Seek(SecHeader.VirtualAddress+SecHeader.VirtualSize,SeekOrigin.Begin);
					return true;
				}
			}

			return false;
		}
	}
}
